import 'dart:async';
import 'dart:io';

import 'package:flutter/services.dart';
import 'package:meta/meta.dart';

import 'hc_connectivity_platform_interface.dart';
import 'utils.dart';
import 'wifi_network.dart';

enum NetworkSecurity { WPA, WEP, NONE }

/// An implementation of [ConnectivityPlatform] that uses method channels.
class MethodChannelConnectivity extends HcConnectivityPlatform {
  /// The method channel used to interact with the native platform.
  @visibleForTesting
  MethodChannel methodChannel = MethodChannel('plugins.hucii.com/connectivity');

  /// The event channel used to receive ConnectivityResult changes from the native platform.
  @visibleForTesting
  EventChannel eventChannel =
      EventChannel('plugins.hucii.com/connectivity_status');

  Stream<List<WifiNetwork>>? _onConnectivityChanged;

  /// Fires whenever the connectivity state changes.
  Stream<List<WifiNetwork>> get onConnectivityChanged {
    if (_onConnectivityChanged == null) {
      _onConnectivityChanged =
          eventChannel.receiveBroadcastStream().map((dynamic result) {
        return result != null ? result.toString() : "[]";
      }).map(parseConnectivityResult);
    }
    return _onConnectivityChanged!;
  }

  @override
  Future<List<WifiNetwork>> checkConnectivity() async {
    final String checkResult =
        await methodChannel.invokeMethod<String>('check') ?? '';
    return parseConnectivityResult(checkResult);
  }

  @override
  Future<String?> getWifiName() async {
    String? wifiName = await methodChannel.invokeMethod<String>('wifiName');
    // as Android might return <unknown ssid>, uniforming result
    // our iOS implementation will return null
    if (wifiName == '<unknown ssid>') {
      wifiName = null;
    }
    return wifiName;
  }

  @override
  Future<String?> getWifiBSSID() {
    return methodChannel.invokeMethod<String>('wifiBSSID');
  }

  @override
  Future<String?> getWifiIP() {
    return methodChannel.invokeMethod<String>('wifiIPAddress');
  }

  @override
  Future<bool> isEnabled() async {
    return (await methodChannel.invokeMethod<bool>('isEnabled')) ?? false;
  }

  @override
  void setEnabled(bool state) {
    Map<String, bool> htArguments = Map();
    htArguments["state"] = state;
    methodChannel.invokeMethod('setEnabled', htArguments);
  }

  @override
  Future<bool> isConnected() async {
    return (await methodChannel.invokeMethod<bool>('isConnected')) ?? false;
  }

  @override
  Future<bool> disconnect() async {
    return (await methodChannel.invokeMethod<bool>('disconnect')) ?? false;
  }

  @override
  Future<bool> connect(
    String ssid, {
    String? password,
    NetworkSecurity security = NetworkSecurity.NONE,
    bool joinOnce = true,
    bool withInternet = false,
  }) async {
    return (await methodChannel.invokeMethod<bool>('connect', {
          "ssid": ssid.toString(),
          "password": password ?? '',
          "join_once": joinOnce,
          "with_internet": withInternet,
          "security":
              security.toString().substring('$NetworkSecurity'.length + 1),
        })) ??
        false;
  }

  @override
  Future<bool> forceWifiUsage(bool useWifi) async {
    Map<String, bool> htArguments = Map();
    htArguments["useWifi"] = useWifi;
    return (await methodChannel.invokeMethod('forceWifiUsage', htArguments)) ??
        false;
  }

  @override
  Future<bool> registerWifiNetwork(
    String ssid, {
    String? password,
    NetworkSecurity security = NetworkSecurity.NONE,
  }) async {
    if (!Platform.isIOS && !await isEnabled()) {
      setEnabled(true);
    }
    bool bResult = false;
    try {
      bResult = await methodChannel.invokeMethod('registerWifiNetwork', {
        "ssid": ssid.toString(),
        "password": password ?? '',
        "security":
            security.toString().substring('$NetworkSecurity'.length + 1),
      });
    } on MissingPluginException catch (e) {
      print("MissingPluginException : ${e.toString()}");
    }
    return bResult;
  }

  @override
  Future<bool> removeWifiNetwork(String ssid) async {
    Map<String, String> htArguments = Map();
    htArguments["ssid"] = ssid;
    return (await methodChannel.invokeMethod(
            'removeWifiNetwork', htArguments)) ??
        false;
  }

  @override
  Future<String?> openWifiNetwork() {
    return methodChannel.invokeMethod('openWifiNetwork');
  }
}
